//! Nuclei POC 解析器和执行引擎

use anyhow::{anyhow, Result};
use good_mitm_passive_scan::{MatchTarget, RuleMatcher, ScanResult, Severity};
use log::{debug, info, warn};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use async_trait::async_trait;

/// Nuclei 模板
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiTemplate {
    /// 模板 ID
    pub id: String,
    /// 模板信息
    pub info: NucleiInfo,
    /// HTTP 请求
    #[serde(default)]
    pub http: Vec<NucleiHttpRequest>,
    /// DNS 请求
    #[serde(default)]
    pub dns: Vec<NucleiDnsRequest>,
    /// Network 请求
    #[serde(default)]
    pub network: Vec<NucleiNetworkRequest>,
}

/// 模板信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiInfo {
    /// 名称
    pub name: String,
    /// 作者
    #[serde(default)]
    pub author: String,
    /// 严重程度
    pub severity: String,
    /// 描述
    #[serde(default)]
    pub description: String,
    /// 参考链接
    #[serde(default)]
    pub reference: Vec<String>,
    /// 标签
    #[serde(default)]
    pub tags: Vec<String>,
    /// 分类
    #[serde(default)]
    pub classification: Option<NucleiClassification>,
}

/// 分类信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiClassification {
    #[serde(rename = "cvss-metrics")]
    pub cvss_metrics: Option<String>,
    #[serde(rename = "cvss-score")]
    pub cvss_score: Option<f32>,
    #[serde(rename = "cve-id")]
    pub cve_id: Option<String>,
    #[serde(rename = "cwe-id")]
    pub cwe_id: Option<String>,
}

/// HTTP 请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiHttpRequest {
    /// 请求方法
    #[serde(default)]
    pub method: String,
    /// 请求路径
    #[serde(default)]
    pub path: Vec<String>,
    /// 请求头
    #[serde(default)]
    pub headers: HashMap<String, String>,
    /// 请求体
    #[serde(default)]
    pub body: Option<String>,
    /// 匹配器
    #[serde(default)]
    pub matchers: Vec<NucleiMatcher>,
    /// 提取器
    #[serde(default)]
    pub extractors: Vec<NucleiExtractor>,
}

/// DNS 请求（简化版）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiDnsRequest {
    pub name: String,
    #[serde(rename = "type")]
    pub dns_type: String,
}

/// Network 请求（简化版）
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiNetworkRequest {
    pub inputs: Vec<String>,
}

/// 匹配器
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiMatcher {
    /// 匹配器类型 (word, regex, status, dsl, binary)
    #[serde(rename = "type")]
    pub matcher_type: String,
    /// 匹配条件 (and, or)
    #[serde(default = "default_condition")]
    pub condition: String,
    /// 词语匹配
    #[serde(default)]
    pub words: Vec<String>,
    /// 正则表达式
    #[serde(default)]
    pub regex: Vec<String>,
    /// 状态码
    #[serde(default)]
    pub status: Vec<u16>,
    /// DSL 表达式
    #[serde(default)]
    pub dsl: Vec<String>,
    /// 匹配部分 (header, body, all)
    #[serde(default = "default_part")]
    pub part: String,
}

fn default_condition() -> String {
    "or".to_string()
}

fn default_part() -> String {
    "body".to_string()
}

/// 提取器
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NucleiExtractor {
    /// 提取器类型
    #[serde(rename = "type")]
    pub extractor_type: String,
    /// 提取部分
    #[serde(default)]
    pub part: String,
    /// 正则表达式
    #[serde(default)]
    pub regex: Vec<String>,
    /// 组号
    #[serde(default)]
    pub group: Option<usize>,
}

/// Nuclei 模板加载器
pub struct NucleiTemplateLoader {
    templates: Vec<NucleiTemplate>,
}

impl NucleiTemplateLoader {
    /// 创建新的模板加载器
    pub fn new() -> Self {
        Self {
            templates: Vec::new(),
        }
    }

    /// 从目录加载所有模板
    pub fn load_from_directory<P: AsRef<Path>>(&mut self, dir: P) -> Result<usize> {
        info!("Loading Nuclei templates from directory: {:?}", dir.as_ref());

        let mut count = 0;

        for entry in std::fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();

            if path.is_file() && (path.extension().and_then(|s| s.to_str()) == Some("yaml")
                || path.extension().and_then(|s| s.to_str()) == Some("yml")) {
                match self.load_template(&path) {
                    Ok(_) => count += 1,
                    Err(e) => warn!("Failed to load template {:?}: {}", path, e),
                }
            } else if path.is_dir() {
                // 递归加载子目录
                count += self.load_from_directory(&path)?;
            }
        }

        info!("Loaded {} Nuclei templates", count);

        Ok(count)
    }

    /// 加载单个模板文件
    pub fn load_template<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        let content = std::fs::read_to_string(&path)?;
        let template: NucleiTemplate = serde_yaml::from_str(&content)
            .map_err(|e| anyhow!("Failed to parse template {:?}: {}", path.as_ref(), e))?;

        debug!("Loaded template: {} ({})", template.id, template.info.name);

        self.templates.push(template);

        Ok(())
    }

    /// 获取所有模板
    pub fn get_templates(&self) -> &[NucleiTemplate] {
        &self.templates
    }

    /// 按 ID 查找模板
    pub fn find_template(&self, id: &str) -> Option<&NucleiTemplate> {
        self.templates.iter().find(|t| t.id == id)
    }

    /// 按标签过滤模板
    pub fn filter_by_tags(&self, tags: &[String]) -> Vec<&NucleiTemplate> {
        self.templates
            .iter()
            .filter(|t| t.info.tags.iter().any(|tag| tags.contains(tag)))
            .collect()
    }

    /// 按严重程度过滤
    pub fn filter_by_severity(&self, severity: &str) -> Vec<&NucleiTemplate> {
        self.templates
            .iter()
            .filter(|t| t.info.severity.eq_ignore_ascii_case(severity))
            .collect()
    }
}

/// Nuclei 规则匹配器
pub struct NucleiMatcher {
    loader: NucleiTemplateLoader,
    matcher_id: String,
}

impl NucleiMatcher {
    /// 创建新的 Nuclei 匹配器
    pub fn new(matcher_id: String) -> Self {
        Self {
            loader: NucleiTemplateLoader::new(),
            matcher_id,
        }
    }

    /// 加载模板目录
    pub fn load_templates<P: AsRef<Path>>(&mut self, dir: P) -> Result<usize> {
        self.loader.load_from_directory(dir)
    }

    /// 从 GitHub 下载 Nuclei 社区模板
    pub async fn download_community_templates(&self, target_dir: &str) -> Result<()> {
        info!("Downloading Nuclei community templates...");

        // 使用 git clone 下载模板
        let status = tokio::process::Command::new("git")
            .args(&[
                "clone",
                "--depth",
                "1",
                "https://github.com/projectdiscovery/nuclei-templates.git",
                target_dir,
            ])
            .status()
            .await?;

        if !status.success() {
            return Err(anyhow!("Failed to clone nuclei-templates repository"));
        }

        info!("Successfully downloaded Nuclei community templates to {}", target_dir);

        Ok(())
    }

    /// 将 Nuclei 严重程度转换为内部 Severity
    fn convert_severity(nuclei_severity: &str) -> Severity {
        match nuclei_severity.to_lowercase().as_str() {
            "critical" => Severity::Critical,
            "high" => Severity::High,
            "medium" => Severity::Medium,
            "low" => Severity::Low,
            "info" | "informational" => Severity::Info,
            _ => Severity::Info,
        }
    }

    /// 检查匹配器是否匹配
    fn check_matcher(
        &self,
        matcher: &NucleiMatcher,
        status_code: Option<u16>,
        headers: &str,
        body: &str,
    ) -> bool {
        let content = match matcher.part.as_str() {
            "header" => headers,
            "body" => body,
            "all" => &format!("{}\n{}", headers, body),
            _ => body,
        };

        match matcher.matcher_type.as_str() {
            "word" => {
                let results: Vec<bool> = matcher
                    .words
                    .iter()
                    .map(|word| content.contains(word))
                    .collect();

                if matcher.condition == "and" {
                    results.iter().all(|&x| x)
                } else {
                    results.iter().any(|&x| x)
                }
            }
            "regex" => {
                let results: Vec<bool> = matcher
                    .regex
                    .iter()
                    .filter_map(|pattern| Regex::new(pattern).ok())
                    .map(|re| re.is_match(content))
                    .collect();

                if matcher.condition == "and" {
                    results.iter().all(|&x| x)
                } else {
                    results.iter().any(|&x| x)
                }
            }
            "status" => {
                if let Some(code) = status_code {
                    matcher.status.contains(&code)
                } else {
                    false
                }
            }
            _ => false,
        }
    }
}

#[async_trait]
impl RuleMatcher for NucleiMatcher {
    async fn match_event(
        &self,
        event: &good_mitm_passive_scan::HttpEvent,
    ) -> Result<Vec<ScanResult>, good_mitm_passive_scan::ScanError> {
        let mut results = Vec::new();

        let body_str = String::from_utf8_lossy(&event.body);
        let headers_str = event
            .headers
            .iter()
            .map(|(k, v)| format!("{}: {}", k, v))
            .collect::<Vec<_>>()
            .join("\n");

        let status_code = event
            .status_code
            .as_ref()
            .and_then(|s| s.parse::<u16>().ok());

        // 对每个模板进行匹配
        for template in self.loader.get_templates() {
            // 只检查 HTTP 请求类型的模板
            for http_req in &template.http {
                // 检查所有匹配器
                let all_matched = http_req.matchers.iter().all(|matcher| {
                    self.check_matcher(matcher, status_code, &headers_str, &body_str)
                });

                if all_matched {
                    // 创建扫描结果
                    results.push(ScanResult {
                        id: uuid::Uuid::new_v4().to_string(),
                        rule_id: template.id.clone(),
                        rule_name: template.info.name.clone(),
                        severity: Self::convert_severity(&template.info.severity),
                        url: event.metadata.url.clone(),
                        host: event.metadata.host.clone(),
                        path: event.metadata.path.clone(),
                        method: event.metadata.method.clone(),
                        matched_content: Some(body_str.chars().take(500).collect()),
                        evidence: Some(format!(
                            "Matched Nuclei template: {} ({})",
                            template.id, template.info.name
                        )),
                        timestamp: chrono::Utc::now(),
                        metadata: {
                            let mut meta = HashMap::new();
                            meta.insert("template_id".to_string(), template.id.clone());
                            meta.insert("author".to_string(), template.info.author.clone());
                            meta.insert("description".to_string(), template.info.description.clone());
                            if !template.info.tags.is_empty() {
                                meta.insert("tags".to_string(), template.info.tags.join(", "));
                            }
                            meta
                        },
                    });
                }
            }
        }

        Ok(results)
    }

    async fn get_rule_count(&self) -> usize {
        self.loader.get_templates().len()
    }

    async fn reload(&mut self) -> Result<(), good_mitm_passive_scan::ScanError> {
        // Nuclei 模板重新加载逻辑可以在这里实现
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_template_loader_creation() {
        let loader = NucleiTemplateLoader::new();
        assert_eq!(loader.get_templates().len(), 0);
    }

    #[test]
    fn test_severity_conversion() {
        assert_eq!(
            NucleiMatcher::convert_severity("critical"),
            Severity::Critical
        );
        assert_eq!(NucleiMatcher::convert_severity("high"), Severity::High);
        assert_eq!(NucleiMatcher::convert_severity("info"), Severity::Info);
    }
}
